﻿/**
 * Pool
 *
 * @author		Allen Chou
 * @version		1.0.0 (last update: Dec 13 2008)
 * @link        http://cjcat.blogspot.com
 * @link		http://cjcat2266.deviantart.com
 */

package idv.cjcat.ds {
	import flash.errors.IllegalOperationError;
	
	/**
	 * A <code>Pool</code> object represents a pool that allows you to store objects in.
	 * <p>
	 * Use a pool to get and recycle objects if you are using this type of objects frequently,
	 * instead of frequently instantiating new ones and getting rid of old ones.
	 * Because the latter consumes greater CPU resource.
	 * </p>
	 * <p>
	 * Once the capacity of a pool is set through the constructor, it cannot be changed.
	 * However, dynamic capacity will be implemented in the future.
	 * </p>
	 */
	public class Pool {
		
		private var _numObjects:uint;
		
		private var _type:Class;
		private var _capacity:uint;
		private var _list:List;
		private var _iterator:ListIterator;
		private var _lastOperationIsGet:Boolean = false;
		
		/**
		 * Creates a pool.
		 * @param	type         The type of objects the pool contains.
		 * @param	capacity     The maximum number of objects the pool can contain.
		 */
		public function Pool(type:Class, capacity:uint) {
			if (capacity == 0) throw new IllegalOperationError("The capacity of a pool must be positive");
			
			_type = type;
			_numObjects = _capacity = capacity;
			_list = new List();
			for (var i:uint = 0; i < _capacity; i++) _list.push(new _type());
			_iterator = _list.getIterator() as ListIterator;
		}
		
		/**
		 * Returns an object in the pool.
		 * @return The object.
		 */
		public function get():Object {
			if (_iterator.node == null) {
				if (_lastOperationIsGet) throw new IllegalOperationError("The pool is empty.");
				else _iterator.begin();
			}
			
			var temp:Object = _iterator.data;
			_iterator._node.data = null;
			_iterator.next();
			
			_numObjects--;
			_lastOperationIsGet = true;
			return temp;
		}
		
		/**
		 * Recycles an object back into the pool.
		 * Note that after recycling the object, you must manually remove all the other references to the object for safety.
		 * @param	obj  The object.
		 */
		public function recycle(obj:Object):void {
			if (!(obj is _type)) throw new IllegalOperationError("The type of the object cannot be stored in the pool.");
			
			if (_iterator.node == null) {
				if (!_lastOperationIsGet) throw new IllegalOperationError("The pool is full.");
				else _iterator.end();
			}
			if (_iterator.node.prev == null) throw new IllegalOperationError("The pool is full.");
			
			if (_iterator.node.data == null) {
				_iterator.node.data = obj;
			} else {
				_iterator.prev();
				_iterator.node.data = obj;
			}
			
			_numObjects++;
			_lastOperationIsGet = false;
		}
		
		/**
		 * Determins if the pool is empty.
		 * @return True if the pool is empty, false otherwise.
		 */
		public function isEmpty():Boolean {
			if (_iterator.node == null) {
				if (_lastOperationIsGet) return true;
			}
			return false;
		}
		
		/**
		 * Determins if the pool is full.
		 * @return True if the pool is full, false otherwise.
		 */
		public function isFull():Boolean {
			if (_iterator.node == null) {
				if (!_lastOperationIsGet) return true;
			}
			return false;
		}
		
		/**
		 * The maximum number of objects the pool can contain.
		 */
		public function get capacity():uint { return _capacity; }
		/**
		 * The number of available objects in the pool.
		 */
		public function get numObjects():uint { return _numObjects; }
	}
}